#!/usr/bin/env python
# -*- coding:utf-8 -*-

# vi:ts=4 sw=4 et

import matplotlib.pyplot as pyplot
import time

from random import expovariate

from fila_de_eventos import FilaDeEventos
from evento import Upload, ChegadaExternaPeer, Saida
from usuario import Usuario

DEBUG = False

class Simulador(object):
    """Classe que encapsula um simulador
    """

    def __init__(self, b, mu, u, gamma, n0, p, lambida, blocos_iniciais, tam_rodada, n_rodadas, eventos_transientes, dt):
        """Inicialização dos parâmetros do simulador
        """
        self.b = b
        self.mu = mu
        self.u = u
        self.gamma = gamma
        self.n0 = n0
        self.p = p
        self.lambida = lambida
        self.blocos_iniciais = blocos_iniciais

        self.tempo_agora = 0
        self.eventos = FilaDeEventos()
        self.seeds = []
        self.peers = []
        self.seeds_sairam = []
        self.publisher = None

        self.tam_rodada = tam_rodada
        self.n_rodadas = n_rodadas

        self.eventos_transientes = eventos_transientes

        self.dt = dt
        self.anotacao_tempo = []


    def debug_print(self, string):
        """Função de impressão, para debug
        """
        if DEBUG:
            print string


    def start(self):
        """Prepara o simulador, inicializando algumas variáveis e
           gerando os eventos iniciais
        """

        self.rodada = 0

        #criando o arquivo de acordo com o número de blocos
        self.arquivo = range(0, self.b)

        #criando o publisher
        self.publisher = Usuario(arquivo=self.arquivo, classe='publisher', nome='0')
        self.publisher.chegada = self.tempo_agora

        #no início não há seeds
        self.seeds = []

        #criando os peers de acordo com n0 (população inicial)
        self.peers = []
        blocos = range(0, self.blocos_iniciais)

        for i in range(0, self.n0):
            peer = Usuario(arquivo=self.arquivo, classe='peer', nome='%d' % i, blocos=blocos)
            peer.chegada = self.tempo_agora
            peer.rodada_entrou = self.rodada
            self.peers.append(peer)

        #no início somente o publisher pode gerar eventos de upload
        """1º parâmetro: tempo pro qual o evento foi "agendado"
           2º parâmetro: evento em si
        """
        self.eventos.adicionar(self.publisher.proximo_evento(self.u), Upload(self.publisher)) #[?] não entendi o 2º parâmetro...
        
        #agendando chegada externa de peer
        if self.lambida > 0:
            dt = expovariate(lambd=self.lambida)
            self.eventos.adicionar(self.lambida, ChegadaExternaPeer())
        

    def run(self):
        """Executa o loop principal do simulador, processando todos os eventos
        """
        self.anotacao_tempo.append((0, self.n0))
        t = 0
        while self.rodada <= self.n_rodadas: 
            i_evento = 0
            while 1:
                #o método self.eventos.remover() retorna a tupla (tempo, evento) para o evento a ser tratado
                self.tempo_agora, evento = self.eventos.remover()
                
                #anotando peers no tempo t
                ultima_anotacao = self.anotacao_tempo[-1][0]
                dt_passados = int((self.tempo_agora - ultima_anotacao) / self.dt)

                for i in range(1, dt_passados+1):
                    self.anotacao_tempo.append((ultima_anotacao + i * self.dt, len(self.peers)))

                if self.rodada == 0:
                    #fase transiente
                    if self.tempo_agora > self.eventos_transientes:
                        self.rodada += 1
                        print 'acabou fase transiente'
                        evento.processar(self)
                        break
                else:
                    #rodadas
                    if i_evento > self.tam_rodada:
                        print 'acabou rodada %d' % self.rodada
                        self.rodada += 1
                        evento.processar(self)
                        break
                
                evento.processar(self)
                i_evento += 1


